package org.vt.eldercare;

import java.awt.EventQueue;

import javax.swing.JFrame;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.RowSpec;
import com.jgoodies.forms.factories.FormFactory;
import java.awt.event.WindowEvent;
import java.awt.Insets;
import java.awt.Label;
import java.awt.Rectangle;

import org.jfree.chart.ChartPanel;
import org.vt.eldercare.console.ConsoleWindow;
import org.vt.eldercare.data.GUI;
import org.vt.eldercare.data.Node;
import org.vt.eldercare.data.NodeStatusChangeListener;
import org.vt.eldercare.data.SampleRateDisplayRunnable;
import org.vt.eldercare.lock.PortLock;
import org.vt.eldercare.serial.SerialWriter;
import org.vt.eldercare.serial.TwoWaySerialComm;
import org.vt.eldercare.serial.USBPortListener;
import org.vt.eldercare.serial.USBPortRunnable;
import org.vt.eldercare.ui.CloseListener;
import org.vt.eldercare.ui.HelpWindow;
import org.vt.eldercare.ui.NodeConfigurationDialog;
import org.vt.eldercare.ui.NodeJLabel;
import org.vt.eldercare.ui.NodeListModel;
import org.vt.eldercare.ui.NodeStatusDisplay;
import org.vt.eldercare.ui.RightClickOnNodeListener;
import org.vt.eldercare.ui.SampleRateJLabel;
import org.vt.eldercare.ui.TextFilter;
import org.vt.eldercare.ui.Utils;

import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JList;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner.DefaultEditor;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.text.ParseException;

import javax.swing.JButton;
import javax.swing.JTabbedPane;
import java.awt.Dimension;
import javax.swing.JLabel;
import java.awt.Color;
import javax.swing.border.BevelBorder;
import javax.swing.Box;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Component;
import javax.swing.border.MatteBorder;
import javax.swing.JScrollPane;
import java.awt.Toolkit;

public class ElderCareGUI implements GUI, USBPortListener,
		NodeStatusChangeListener, NodeStatusDisplay {

	private static final String ABOUT_HOKIESENSOR_TEXT = "HokieSensor GUI v1.01";
	private static final String PORT_UNPLUGGED_TEXT = "The USB Access Point has been unplugged.";
	private static final String PORT_CONNECTED_TEXT = "USB Access Point connected on port ";

	private static final int GRAPH_GAP_SPACE = 7;

	private JFrame ElderCareGUI;
	private final Label sensorListLabel = new Label("Available Nodes:");
	private final NodeListModel nodeListModel = new NodeListModel();
	private final JList<NodeJLabel> nodeList = new JList<NodeJLabel>();
	private final JMenuBar menuBar = new JMenuBar();
	private final JMenu mnFile = new JMenu("File");
	private final JMenuItem mntmClosealtf = new JMenuItem("Exit (Alt+F4)");
	private final JMenu mnView = new JMenu("View");
	private final JCheckBoxMenuItem mntmConsole = new JCheckBoxMenuItem(
			"Console");
	private final JMenu mnHelp = new JMenu("Help");
	private final JMenuItem mntmShowHelp = new JMenuItem("Show Help");
	private final JMenuItem mntmAboutEldercare = new JMenuItem(
			"About HokieSensor");
	private final JMenu mnExport = new JMenu("Export Data For Node");
	private TwoWaySerialComm usbComm;
	private final JButton disableGyroButton = new JButton("Disable Gyro");
	private final JMenu mnEdit = new JMenu("Edit");
	private final JMenuItem mntmNodeConfiguration = new JMenuItem(
			"Node Configuration...");

	private boolean consoleOpen = false;
	private JFrame consoleWindow;
	private Node curNode = new Node(-1);
	private boolean splitView = false;
	private Thread sampleRateDisplayThread = null;


	public void setConsoleOpen(boolean consoleOpen) {
		this.consoleOpen = consoleOpen;
		mntmConsole.setSelected(consoleOpen);
	}

	private final JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
	private ChartPanel gyroChartPanel = curNode.getGyroPanel();
	private ChartPanel accelChartPanel = curNode.getAccelPanel();
	private final JMenuItem mntmSplitView = new JMenuItem("Split View");
	private final Label samplesPerSecondLabel = new Label(
			"Measured Samples Per Second:");
	private final JLabel actualSampleRateLabel = new SampleRateJLabel("N/A");
	private final JPanel actualSampleRatePanel = new JPanel();
	private final Box chartsVerticalBox = Box.createVerticalBox();
	private final Label sampleRateSettingLabel = new Label(
			"Sample Rate Setting (Per Second):");
	private final JSpinner setSampleRateSpinner = new JSpinner();
	private final Box accelHorizontalBox = Box.createHorizontalBox();
	private final JLabel accelStatusLabel = new JLabel("Accelerometer Status:");
	private final JLabel gyroStatusLabel = new JLabel("Gyroscope Status:");
	private final JPanel accelXPanel = new JPanel();
	private final JPanel accelYPanel = new JPanel();
	private final JPanel accelZPanel = new JPanel();
	private final JLabel accelXLbl = new JLabel(
			"<html><font size=\"4\"><b>X</b></font></html>");
	private final JLabel accelYLbl = new JLabel(
			"<html><font size=\"4\"><b>Y</b></font></html>");
	private final JLabel accelZLbl = new JLabel(
			"<html><font size=\"4\"><b>Z</b></font></html>");
	private final Component accelHorizontalStrut1 = Box
			.createHorizontalStrut(5);
	private final Component accelHorizontalStrut2 = Box
			.createHorizontalStrut(5);
	private final Box gyroHorizontalBox = Box.createHorizontalBox();
	private final JPanel gyroXPanel = new JPanel();
	private final JLabel gyroXLbl = new JLabel(
			"<html><font size=\"4\"><b>X</b></font></html>");
	private final Component gyroHorizontalStrut1 = Box.createHorizontalStrut(5);
	private final JPanel gyroYPanel = new JPanel();
	private final JLabel gyroYLbl = new JLabel(
			"<html><font size=\"4\"><b>Y</b></font></html>");
	private final Component gyroHorizontalStrut2 = Box.createHorizontalStrut(5);
	private final JPanel gyroZPanel = new JPanel();
	private final JLabel gyroZLbl = new JLabel(
			"<html><font size=\"4\"><b>Z</b></font></html>");
	private final Box keyHorizontalBox = Box.createHorizontalBox();
	private final JPanel enabledKeyPanel = new JPanel();
	private final JLabel enabledKeyLabel = new JLabel("Enabled");
	private final JPanel disabledKeyPanel = new JPanel();
	private final JLabel disabledKeyLabel = new JLabel("Disabled");
	private final Component keyHorizontalStrut1 = Box.createHorizontalStrut(5);
	private final Component keyHorizontalStrut2 = Box.createHorizontalStrut(5);
	private final JPanel pendingKeyPanel = new JPanel();
	private final JLabel pendingKeyLabel = new JLabel("Pending");
	private final JLabel keyLabel = new JLabel("Key:");
	private final JScrollPane nodeListScrollPane = new JScrollPane();
	

	JFrame frame = new JFrame();
	java.net.URL helpURL;
	JEditorPane htmlPane;
	String getURL;

	public TwoWaySerialComm getUsbComm() {
		return usbComm;
	}

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		PortLock.checkRun();
		
		try {
			// Set System L&F
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (UnsupportedLookAndFeelException e) {
			// handle exception
		} catch (ClassNotFoundException e) {
			// handle exception
		} catch (InstantiationException e) {
			// handle exception
		} catch (IllegalAccessException e) {
			// handle exception
		}

		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					ElderCareGUI window = new ElderCareGUI();
					window.ElderCareGUI.setVisible(true);
					window.usbComm = new TwoWaySerialComm();
					window.usbComm.setGUI(window);
					// TODO: Dummy or USB?
					Thread t = new Thread(new USBPortRunnable(window.usbComm,
							window));
					t.start();
					// window.usbComm.connect(new SerialDummyAP());
					ConsoleWindow.setParent(window);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public ElderCareGUI() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		ElderCareGUI = new JFrame();
		ElderCareGUI.setIconImage(Toolkit.getDefaultToolkit().getImage(
				ElderCareGUI.class
						.getResource("/org/vt/eldercare/hokieBird.png")));
		ElderCareGUI.setPreferredSize(new Dimension(879, 477));
		ElderCareGUI.setMinimumSize(new Dimension(879, 477));
		ElderCareGUI.setTitle("HokieSensor GUI");
		ElderCareGUI.setBounds(100, 100, 879, 477);
		ElderCareGUI.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		ElderCareGUI.getContentPane().setLayout(
				new FormLayout(new ColumnSpec[] {
						FormFactory.RELATED_GAP_COLSPEC,
						FormFactory.DEFAULT_COLSPEC,
						FormFactory.RELATED_GAP_COLSPEC,
						ColumnSpec.decode("default:grow"),
						FormFactory.RELATED_GAP_COLSPEC,
						ColumnSpec.decode("center:default"), }, new RowSpec[] {
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						RowSpec.decode("default:grow"),
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC,
						RowSpec.decode("default:grow"),
						FormFactory.RELATED_GAP_ROWSPEC,
						FormFactory.DEFAULT_ROWSPEC,
						FormFactory.RELATED_GAP_ROWSPEC, }));

		ElderCareGUI.addWindowListener(new CloseListener(
				"Are you sure you want to exit?", "Exit ElderCare", usbComm));

		ElderCareGUI.setJMenuBar(menuBar);

		menuBar.add(mnFile);

		mntmClosealtf.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				ElderCareGUI.dispatchEvent(new WindowEvent(ElderCareGUI,
						WindowEvent.WINDOW_CLOSING));
			}
		});

		mnFile.add(mnExport);
		
		mnFile.addSeparator();

		mnFile.add(mntmClosealtf);

		menuBar.add(mnEdit);
		mntmNodeConfiguration.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				NodeConfigurationDialog dialog = new NodeConfigurationDialog(
						ElderCareGUI, curNode);
				dialog.setLocationRelativeTo(ElderCareGUI);
				dialog.setVisible(true);
			}
		});

		mnEdit.add(mntmNodeConfiguration);

		menuBar.add(mnView);
		mntmConsole.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (!consoleOpen) {
					consoleWindow = ConsoleWindow.create();
					consoleOpen = true;
				} else {
					consoleWindow.dispatchEvent(new WindowEvent(consoleWindow,
							WindowEvent.WINDOW_CLOSING));
					consoleOpen = false;
				}
			}
		});
		mntmSplitView.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (splitView) {
					chartsVerticalBox.removeAll();
					chartsVerticalBox.add(tabbedPane);
					tabbedPane.addTab("Accelerometer", null, accelChartPanel,
							null);
					tabbedPane.addTab("Gyroscope", null, gyroChartPanel, null);
					ElderCareGUI.repaint();
					mntmSplitView.setText("Split View");
					splitView = false;
				} else {
					tabbedPane.remove(accelChartPanel);
					tabbedPane.remove(gyroChartPanel);
					chartsVerticalBox.remove(tabbedPane);
					chartsVerticalBox.add(accelChartPanel);
					chartsVerticalBox.add(Box
							.createVerticalStrut(GRAPH_GAP_SPACE));
					chartsVerticalBox.add(gyroChartPanel);
					ElderCareGUI.repaint();
					mntmSplitView.setText("Tabbed View");
					splitView = true;
				}
			}
		});

		mnView.add(mntmSplitView);

		mnView.add(mntmConsole);

		menuBar.add(mnHelp);
		
		mntmShowHelp.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
			HelpWindow.createWindow();
			}
		
		});

		mnHelp.add(mntmShowHelp);
		
		mntmAboutEldercare.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JTextArea textArea = new JTextArea(ABOUT_HOKIESENSOR_TEXT);
				textArea.setFont(UIManager.getDefaults().getFont(
						"TabbedPane.font"));
				textArea.setLineWrap(true);
				textArea.setWrapStyleWord(true);
				textArea.setMargin(new Insets(5, 5, 5, 5));
				textArea.setBackground(ElderCareGUI.getBackground());
				textArea.setBounds(0, 0, 300, 100);
				JOptionPane
						.showMessageDialog(
								ElderCareGUI,
								textArea,
								"About HokieSensor",
								JOptionPane.INFORMATION_MESSAGE,
								new ImageIcon(Toolkit.getDefaultToolkit()
										.getImage(
												ElderCareGUI.class
														.getResource("/org/vt/eldercare/hokieBird.png"))));
			}
		});

		mnHelp.add(mntmAboutEldercare);

		ElderCareGUI.getContentPane()
				.add(sensorListLabel, "2, 4, left, bottom");
		nodeListScrollPane.setViewportBorder(new BevelBorder(
				BevelBorder.LOWERED, null, null, null, null));

		ElderCareGUI.getContentPane().add(nodeListScrollPane,
				"2, 6, 1, 5, fill, fill");
		nodeListScrollPane.setViewportView(nodeList);
		nodeList.setBorder(null);
		nodeList.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				Rectangle r = nodeList.getCellBounds(0,
						nodeList.getLastVisibleIndex());
				if (r != null && r.contains(e.getPoint())) {
					int index = nodeList.locationToIndex(e.getPoint());
					nodeListModel.getElementAt(index).dispatchEvent(e);
				}
			}

			@Override
			public void mousePressed(MouseEvent e) {
				Rectangle r = nodeList.getCellBounds(0,
						nodeList.getLastVisibleIndex());
				if (r != null && r.contains(e.getPoint())) {
					int index = nodeList.locationToIndex(e.getPoint());
					nodeListModel.getElementAt(index).dispatchEvent(e);
				}
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				Rectangle r = nodeList.getCellBounds(0,
						nodeList.getLastVisibleIndex());
				if (r != null && r.contains(e.getPoint())) {
					int index = nodeList.locationToIndex(e.getPoint());
					nodeListModel.getElementAt(index).dispatchEvent(e);
				}
			}
		});

		nodeList.setModel(nodeListModel);

		nodeList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent arg0) {
				if (nodeList.getSelectedIndex() != -1) {
					curNode = usbComm.getNodes().get(
							nodeList.getSelectedIndex());
					for (Node n : usbComm.getNodes()) {
						if (n.equals(curNode)) {
							n.setNodeStatusChangeListener(ElderCareGUI.this);
						} else {
							n.setNodeStatusChangeListener(null);
						}
					}
					loadGraphsForNode(curNode);
				}
			}
		});

		ElderCareGUI.getContentPane().add(samplesPerSecondLabel,
				"6, 6, left, bottom");
		actualSampleRatePanel.setBorder(new BevelBorder(BevelBorder.LOWERED,
				null, null, null, null));
		actualSampleRatePanel.setBackground(Color.WHITE);

		ElderCareGUI.getContentPane().add(actualSampleRatePanel,
				"6, 8, center, center");
		actualSampleRatePanel.add(actualSampleRateLabel);

		ElderCareGUI.getContentPane().add(chartsVerticalBox, "4, 2, 1, 25");
		chartsVerticalBox.add(tabbedPane);

		tabbedPane.addTab("Accelerometer", null, accelChartPanel, null);
		tabbedPane.addTab("Gyroscope", null, gyroChartPanel, null);

		disableGyroButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if (curNode.getStatus().isGyroEnabled()) {
					curNode.disableGyro();
				} else {
					curNode.enableGyro();
				}
			}
		});
		setSampleRateSpinner.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent arg0) {
				try {
					setSampleRateSpinner.commitEdit();
				} catch (ParseException pe) {
					JComponent editor = setSampleRateSpinner.getEditor();
					if (editor instanceof DefaultEditor) {
						((DefaultEditor) editor).getTextField().setValue(
								setSampleRateSpinner.getValue());
					}
				}
				int newSampleRate = (Integer) setSampleRateSpinner.getValue();
				curNode.getStatus().setSamples_per_second(newSampleRate);
				usbComm.serialWrite(curNode.getNodeID(),
						SerialWriter.SAMPLE_RATE(newSampleRate));
			}
		});

		ElderCareGUI.getContentPane().add(keyLabel, "2, 12, default, bottom");

		ElderCareGUI.getContentPane().add(sampleRateSettingLabel,
				"6, 12, left, bottom");

		ElderCareGUI.getContentPane().add(keyHorizontalBox, "2, 14");
		enabledKeyPanel.setBorder(new MatteBorder(1, 1, 1, 1,
				(Color) new Color(0, 0, 0)));
		enabledKeyPanel.setBackground(Color.GREEN);

		keyHorizontalBox.add(enabledKeyPanel);

		enabledKeyPanel.add(enabledKeyLabel);

		keyHorizontalBox.add(keyHorizontalStrut1);
		disabledKeyPanel.setBorder(new MatteBorder(1, 1, 1, 1,
				(Color) new Color(0, 0, 0)));
		disabledKeyPanel.setBackground(Color.RED);

		keyHorizontalBox.add(disabledKeyPanel);

		disabledKeyPanel.add(disabledKeyLabel);

		keyHorizontalBox.add(keyHorizontalStrut2);
		pendingKeyPanel.setBorder(new MatteBorder(1, 1, 1, 1,
				(Color) new Color(0, 0, 0)));
		pendingKeyPanel.setBackground(Color.YELLOW);

		keyHorizontalBox.add(pendingKeyPanel);

		pendingKeyPanel.add(pendingKeyLabel);
		setSampleRateSpinner.setModel(new SpinnerNumberModel(20, 1, 30, 1));

		ElderCareGUI.getContentPane().add(setSampleRateSpinner, "6, 14");

		ElderCareGUI.getContentPane().add(accelStatusLabel,
				"2, 16, default, bottom");
		ElderCareGUI.getContentPane().add(disableGyroButton,
				"6, 16, center, center");

		ElderCareGUI.getContentPane().add(accelHorizontalBox, "2, 18");
		accelXPanel.setBorder(new MatteBorder(1, 1, 1, 1, (Color) new Color(0,
				0, 0)));
		accelXPanel.setBackground(Color.GREEN);

		accelHorizontalBox.add(accelXPanel);

		accelXPanel.add(accelXLbl);

		accelHorizontalBox.add(accelHorizontalStrut1);
		accelYPanel.setBorder(new MatteBorder(1, 1, 1, 1, (Color) new Color(0,
				0, 0)));
		accelYPanel.setBackground(Color.GREEN);

		accelHorizontalBox.add(accelYPanel);

		accelYPanel.add(accelYLbl);

		accelHorizontalBox.add(accelHorizontalStrut2);
		accelZPanel.setBorder(new MatteBorder(1, 1, 1, 1, (Color) new Color(0,
				0, 0)));
		accelZPanel.setBackground(Color.GREEN);

		accelHorizontalBox.add(accelZPanel);

		accelZPanel.add(accelZLbl);

		ElderCareGUI.getContentPane().add(gyroStatusLabel,
				"2, 20, default, bottom");

		ElderCareGUI.getContentPane().add(gyroHorizontalBox, "2, 22");
		gyroXPanel.setBorder(new MatteBorder(1, 1, 1, 1, (Color) new Color(0,
				0, 0)));
		gyroXPanel.setBackground(Color.GREEN);

		gyroHorizontalBox.add(gyroXPanel);

		gyroXPanel.add(gyroXLbl);

		gyroHorizontalBox.add(gyroHorizontalStrut1);
		gyroYPanel.setBorder(new MatteBorder(1, 1, 1, 1, (Color) new Color(0,
				0, 0)));
		gyroYPanel.setBackground(Color.GREEN);

		gyroHorizontalBox.add(gyroYPanel);

		gyroYPanel.add(gyroYLbl);

		gyroHorizontalBox.add(gyroHorizontalStrut2);
		gyroZPanel.setBorder(new MatteBorder(1, 1, 1, 1, (Color) new Color(0,
				0, 0)));
		gyroZPanel.setBackground(Color.GREEN);

		gyroHorizontalBox.add(gyroZPanel);

		gyroZPanel.add(gyroZLbl);
	}


	public void addNode(Node n, boolean firstNode) {
		NodeJLabel l = new NodeJLabel(n.getName());
		JMenu m = createNodeExportMenu(n);
		n.setNodeExportMenu(m);
		l.addMouseListener(new RightClickOnNodeListener(n, ElderCareGUI));
		nodeListModel.addElement(l);
		n.setNodeStatusChangeListener(this);
		n.setNodeNameChangeListener(nodeListModel);
		n.setLabel(l);
		n.setUSBComm(usbComm);
		n.setStatusDisplay(this);
		mnExport.add(m);
		if (firstNode) {
			nodeList.setSelectedIndex(0);
		}
		nodeListScrollPane.setPreferredSize(new Dimension(nodeListScrollPane
				.getWidth(), usbComm.getNodes().size() * l.getHeight()));
		ElderCareGUI.pack();
	}

	private JMenu createNodeExportMenu(final Node n) {
		JMenu returnVal = new JMenu(n.getName());
		JMenuItem temp = new JMenuItem("As Text File...");
		temp.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JFileChooser fc = new JFileChooser((File) null);
				fc.setFileFilter(new TextFilter());
				if (fc.showSaveDialog(ElderCareGUI) == JFileChooser.APPROVE_OPTION) {
					File selectedFile = fc.getSelectedFile();
					try {
						if (Utils.getExtension(selectedFile).equals(Utils.txt)) {
							n.exportDataToFile(selectedFile);
						} else {
							n.exportDataToFile(new File(selectedFile
									.getAbsolutePath() + ".txt"));
						}
					} catch (FileNotFoundException e1) {
						e1.printStackTrace();
					}
				}
			}
		});
		returnVal.add(temp);
		return returnVal;
	}

	private void loadGraphsForNode(Node n) {
		if (sampleRateDisplayThread != null) {
			sampleRateDisplayThread.interrupt();
			n.getTempSampleCount();
		}
		setSampleRateSpinner.setValue(n.getStatus().getSamples_per_second());
		sampleRateDisplayThread = new Thread(new SampleRateDisplayRunnable(n,
				actualSampleRateLabel));
		sampleRateDisplayThread.start();
		nodeStatusChanged(n);
		if (n.getStatus().isGyroEnabled()) {
			disableGyroButton.setText("Disable Gyro");
		} else {
			disableGyroButton.setText("Enable Gyro");
		}
		int tabStatus = -1;
		if (splitView) {
			chartsVerticalBox.removeAll();
		} else {
			tabStatus = tabbedPane.getSelectedIndex();
			tabbedPane.remove(accelChartPanel);
			tabbedPane.remove(gyroChartPanel);
		}
		accelChartPanel = n.getAccelPanel();
		gyroChartPanel = n.getGyroPanel();
		if (splitView) {
			chartsVerticalBox.add(accelChartPanel);
			chartsVerticalBox.add(Box.createVerticalStrut(GRAPH_GAP_SPACE));
			chartsVerticalBox.add(gyroChartPanel);
			ElderCareGUI.revalidate();
			ElderCareGUI.repaint();
		} else {
			tabbedPane.addTab("Accelerometer", null, accelChartPanel, null);
			tabbedPane.addTab("Gyroscope", null, gyroChartPanel, null);
			tabbedPane.setSelectedIndex(tabStatus);
		}
	}

	public void onPortUnplugged() {
		JTextArea textArea = new JTextArea(PORT_UNPLUGGED_TEXT);
		textArea.setFont(UIManager.getDefaults().getFont("TabbedPane.font"));
		textArea.setLineWrap(true);
		textArea.setWrapStyleWord(true);
		textArea.setMargin(new Insets(5, 5, 5, 5));
		textArea.setBackground(ElderCareGUI.getBackground());
		textArea.setBounds(0, 0, 300, 150);
		JOptionPane.showMessageDialog(ElderCareGUI, textArea, "USB Error",
				JOptionPane.ERROR_MESSAGE);
	}

	public void onPortConnected(String portName) {
		JTextArea textArea = new JTextArea(PORT_CONNECTED_TEXT + portName + ".");
		textArea.setFont(UIManager.getDefaults().getFont("TabbedPane.font"));
		textArea.setLineWrap(true);
		textArea.setWrapStyleWord(true);
		textArea.setMargin(new Insets(5, 5, 5, 5));
		textArea.setBackground(ElderCareGUI.getBackground());
		textArea.setBounds(0, 0, 300, 150);
		JOptionPane.showMessageDialog(ElderCareGUI, textArea,
				"USB Notification", JOptionPane.INFORMATION_MESSAGE);
	}

	public void nodeStatusChanged(Node n) {
		setColorOfStatusLED(accelXPanel, n.getStatus().isAccelXEnabled());
		setColorOfStatusLED(accelYPanel, n.getStatus().isAccelYEnabled());
		setColorOfStatusLED(accelZPanel, n.getStatus().isAccelZEnabled());
		setColorOfStatusLED(gyroXPanel, n.getStatus().isGyroXEnabled());
		setColorOfStatusLED(gyroYPanel, n.getStatus().isGyroYEnabled());
		setColorOfStatusLED(gyroZPanel, n.getStatus().isGyroZEnabled());
		if (n.getStatus().isGyroEnabled()) {
			disableGyroButton.setText("Disable Gyro");
		} else {
			disableGyroButton.setText("Enable Gyro");
		}
	}

	private void setColorOfStatusLED(JPanel panel, boolean enabled) {
		if (enabled) {
			panel.setBackground(Color.GREEN);
		} else {
			panel.setBackground(Color.RED);
		}
	}

	public void pendingGyroX() {
		gyroXPanel.setBackground(Color.YELLOW);
	}

	public void pendingGyroY() {
		gyroYPanel.setBackground(Color.YELLOW);
	}

	public void pendingGyroZ() {
		gyroZPanel.setBackground(Color.YELLOW);
	}

	public void pendingAccelX() {
		accelXPanel.setBackground(Color.YELLOW);
	}

	public void pendingAccelY() {
		accelYPanel.setBackground(Color.YELLOW);
	}

	public void pendingAccelZ() {
		accelZPanel.setBackground(Color.YELLOW);
	}

	public void pendingGyro() {
		pendingGyroX();
		pendingGyroY();
		pendingGyroZ();
	}

	public void pendingAccel() {
		pendingAccelX();
		pendingAccelY();
		pendingAccelZ();
	}

	public void nodeSampleRateChanged(Node n) {
		setSampleRateSpinner.setValue(n.getStatus().getSamples_per_second());
	}
}
